#include "SerialPort.h"
#include <WinBase.h>
#include <iostream>

SerialPort::SerialPort(const char *PortName) {
    this->Connected = false;

    this->SerialHandle =
        CreateFileA(static_cast<LPCSTR>(PortName), GENERIC_READ | GENERIC_WRITE,
                    0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (this->SerialHandle == INVALID_HANDLE_VALUE) {
        if (GetLastError() == ERROR_FILE_NOT_FOUND) {
            std::cerr << "ERROR: Handle was not attached.Reason : " << PortName
                      << " not available\n";
        } else {
            std::cerr << "ERROR!!!\n";
        }
    } else {
        DCB dcbSerialParameters = {0};

        if (!GetCommState(this->SerialHandle, &dcbSerialParameters)) {
            std::cerr << "Failed to get current serial parameters\n";
        } else {
            dcbSerialParameters.BaudRate = CBR_115200;
            dcbSerialParameters.ByteSize = 8;
            dcbSerialParameters.StopBits = ONESTOPBIT;
            dcbSerialParameters.Parity = NOPARITY;
            dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;

            if (!SetCommState(SerialHandle, &dcbSerialParameters)) {
                std::cout << "ALERT: could not set serial port parameters\n";
            } else {
                this->Connected = true;
                PurgeComm(this->SerialHandle, PURGE_RXCLEAR | PURGE_TXCLEAR);
                Sleep(WAIT_TIME);
            }
        }
    }
}

SerialPort::~SerialPort() {
    if (this->Connected) {
        this->Connected = false;
        CloseHandle(this->SerialHandle);
    }
}

// Reading bytes from serial port to Buffer
// returns read bytes count, or if error occurs, returns 0.
int SerialPort::ReadSerialPort(const char *Buffer, unsigned int BuffSize) {
    DWORD bytesRead{0};
    unsigned int toRead = 0;

    ClearCommError(this->SerialHandle, &this->Errors, &this->Status);

    if (this->Status.cbInQue > 0) {
        if (this->Status.cbInQue > BuffSize) {
            toRead = BuffSize;
        } else {
            toRead = this->Status.cbInQue;
        }
    }

    memset((void *)Buffer, 0, BuffSize);

    if (ReadFile(this->SerialHandle, (void *)Buffer, toRead, &bytesRead,
                 NULL)) {
        return bytesRead;
    }

    return 0;
}

// Sending provided Buffer to serial port
bool SerialPort::WriteSerialPort(const char *Buffer, unsigned int BuffSize) {
    DWORD bytesSend;

    if (!WriteFile(this->SerialHandle, (void *)Buffer, BuffSize, &bytesSend,
                   0)) {
        ClearCommError(this->SerialHandle, &this->Errors, &this->Status);
        return false;
    }

    return true;
}

// Checking if serial port is Connected
bool SerialPort::IsConnected() {
    if (!ClearCommError(this->SerialHandle, &this->Errors, &this->Status)) {
        this->Connected = false;
    }

    return this->Connected;
}

void SerialPort::CloseSerial() { CloseHandle(this->SerialHandle); }